# -*- coding: utf-8 -*-
"""
xfree.utils.forms
~~~~~~~~~~~~~~~~~~~
处理设置相关的表单操作，包括生成动态的设置表单、填充表单数据、从表单中提取修改后的设置值等，为应用的配置管理等功能提供了便利的表单处理机制。

"""

from enum import Enum

from flask_wtf import FlaskForm
from wtforms import (
    BooleanField,
    FloatField,
    IntegerField,
    SelectField,
    SelectMultipleField,
    StringField,
    validators,
)


class XFreeForm(FlaskForm):
    """
    自定义表单类，用于扩展 FlaskForm 的功能，提供错误信息填充方法。

    1. `populate_errors`方法：
        - 该方法接受一个错误列表作为参数，用于填充表单的错误信息。
        - 遍历错误列表，将错误信息添加到表单的`errors`属性中，并将错误信息添加到对应的字段的错误列表中。
    """

    def populate_errors(self, errors):
        """
        填充表单的错误信息。
        :param errors: 错误列表，包含字段名和错误信息。
        :type errors: list
        :return: None
        """
        # 遍历错误列表
        for attribute, reason in errors:
            # 如果表单的`errors`属性中没有该属性的错误列表，则创建一个空列表
            self.errors.setdefault(attribute, []).append(reason)
            # 获取表单中的对应字段
            field = getattr(self, attribute, None)
            # 如果字段存在
            if field:
                # 将错误信息添加到字段的错误列表中
                field.errors.append(reason)


# 定义一个枚举类，表示设置值的类型
class SettingValueType(Enum):
    """
    设置值类型枚举类。
    定义了各种可能的设置值类型，包括字符串、整数、浮点数、布尔值、单选框和多选框。
    """
    # 字符串类型
    string = 0
    # 整数类型
    integer = 1
    # 浮点数类型
    float = 3
    # 布尔值类型
    boolean = 4
    # 单选框类型
    select = 5
    # 多选框类型
    selectmultiple = 6


# 将表单数据填充到设置字典中的函数
def populate_settings_dict(form, settings):
    """
    将表单数据填充到设置字典中。

    - 遍历设置字典中的每个键值对，检查设置值是否与表单中的对应值相同，如果相同则跳过；如果不同，则将表单中的值更新到新的设置字典中。
    - 返回更新后的设置字典。

    :param form: 表单对象。
    :param settings: 原始设置字典。
    :return: 更新后的设置字典。
    """
    new_settings = {}
    for key, value in settings.items():
        try:
            # 检查值是否发生变化
            if value == form[key].data:
                continue
            else:
                new_settings[key] = form[key].data
        except KeyError:
            pass

    return new_settings


# 将设置字典中的值填充到表单中的函数
def populate_settings_form(form, settings):
    """
    将设置字典中的值填充到表单中。

    - 遍历设置字典中的每个键值对，尝试将设置值填充到表单中的对应字段中，如果出现键错误或值错误则跳过。
    - 返回填充后的表单对象。

    :param form: 表单对象。
    :param settings: 设置字典。
    :return: 填充后的表单对象。
    """
    for key, value in settings.items():
        try:
            form[key].data = value
        except (KeyError, ValueError):
            pass

    return form


# 生成设置表单的函数（待清理）
def generate_settings_form(settings):  # noqa: C901
    """
    生成包含字段验证的设置表单。
    根据传入的设置列表动态生成一个表单类，该表单类包含与设置列表中的每个设置相对应的字段，并根据设置的类型和额外信息添加相应的验证器。

    1. 定义一个名为`SettingsForm`的内部类，继承自`xfreeForm`。
    2. 遍历设置列表中的每个设置。
    3. 根据设置的`value_type`属性确定字段类型，并添加相应的验证器。
    4. 根据设置的类型生成对应的字段，并添加到`SettingsForm`类中。
    5. 返回生成的`SettingsForm`类。
    """


    class SettingsForm(XFreeForm):
        pass

    # 遍历设置列表中的每个设置
    for setting in settings:
        field_validators = []

        # 根据设置值类型确定验证器类
        if setting.value_type in {SettingValueType.integer, SettingValueType.float}:
            validator_class = validators.NumberRange
        elif setting.value_type == SettingValueType.string:
            validator_class = validators.Length

        # 生成验证器
        if "min" in setting.extra:
            # 最小数量验证器
            field_validators.append(validator_class(min=setting.extra["min"]))

        if "max" in setting.extra:
            # 最大数量验证器
            field_validators.append(validator_class(max=setting.extra["max"]))

        # 根据设置类型生成字段
        # IntegerField
        if setting.value_type == SettingValueType.integer:
            setattr(
                SettingsForm,
                setting.key,
                IntegerField(
                    setting.name,
                    validators=field_validators,
                    description=setting.description,
                ),
            )
        # FloatField
        elif setting.value_type == SettingValueType.float:
            setattr(
                SettingsForm,
                setting.key,
                FloatField(
                    setting.name,
                    validators=field_validators,
                    description=setting.description,
                ),
            )

        # StringField
        elif setting.value_type == SettingValueType.string:
            setattr(
                SettingsForm,
                setting.key,
                StringField(
                    setting.name,
                    validators=field_validators,
                    description=setting.description,
                ),
            )

        # SelectMultipleField
        elif setting.value_type == SettingValueType.selectmultiple:
            # 如果没有找到 coerce，则默认为 unicode
            if "coerce" in setting.extra:
                coerce_to = setting.extra["coerce"]
            else:
                coerce_to = str

            setattr(
                SettingsForm,
                setting.key,
                SelectMultipleField(
                    setting.name,
                    choices=setting.extra["choices"](),
                    coerce=coerce_to,
                    description=setting.description,
                ),
            )

        # SelectField
        elif setting.value_type == SettingValueType.select:
            # 如果没有找到 coerce，则默认为 unicode
            if "coerce" in setting.extra:
                coerce_to = setting.extra["coerce"]
            else:
                coerce_to = str

            setattr(
                SettingsForm,
                setting.key,
                SelectField(
                    setting.name,
                    coerce=coerce_to,
                    choices=setting.extra["choices"](),
                    description=setting.description,
                ),
            )

        # BooleanField
        elif setting.value_type == SettingValueType.boolean:
            setattr(
                SettingsForm,
                setting.key,
                BooleanField(setting.name, description=setting.description),
            )

    return SettingsForm
